//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<GameApp.cpp>
///	@path	~/src/appear/xwin/
///	@date	2007/07/03
///	@desc	.

#include "config/config.h"

#include "lib/utilities/observer.h"
#include "lib/utilities/time.h"

#include "graphics/IDevice.h"

#include "engine/xcore/xeres_engine.h"

#include "appear/xwin/GameApp.h"

namespace xeres {

	// atom
	ATOM GameApp::s_procAtom = NULL;

	// engine
	Engine& GameApp::engine( void )
	{
		return Engine::instance();
	}

	// ctor
	GameApp::GameApp( void )
		: m_oldProc( NULL )
		, m_msgFilterDirt( false )
		, m_tickerDirt( false )
	{
		// initialize atom
		if( s_procAtom == NULL )
			s_procAtom = ::GlobalAddAtom( _S("xeres_appear_GameApp_Atom") );
	}

	// dtor
	GameApp::~GameApp( void )
	{
	}

	// initialize
	bool GameApp::Initialize( void )
	{
		return true;
	}

	// finalize
	void GameApp::Finalize( void )
	{
	}

	bool GameApp::AddFilter( const WString& name , const WString& dep , MessageFilter * filter )
	{
		if( m_msgFiltersStage.insert_node( name , dep , filter ) )
		{
			m_msgFilterDirt = true;
			return true;
		}
		return false;
	}

	bool GameApp::AddTicker( const WString& name , const WString& dep , Ticker * ticker )
	{
		if( m_tickersStage.insert_node( name , dep , ticker ) )
		{
			m_tickerDirt = true;
			return true;
		}
		return false;
	}

	namespace
	{
		// startup filter to filter out finalize call
		template<
			typename _QueueType
		>
		class _QueueStageFilter
		{
		public:
			// ctor
			_QueueStageFilter( std::deque< _QueueType >& theQueue )
				: m_queue( theQueue )
			{
			}

			// copier
			_QueueStageFilter( const _QueueStageFilter& rhs )
				: m_queue( rhs.m_queue )
			{
			}

			// nodes
			DepStagesFilterAction operator () ( const WString& path , const WString& name , _QueueType content )
			{
				m_queue.push_back( content );
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

		private:

			// data

			// per-thread call
			std::deque< _QueueType >& m_queue;
		};

		typedef dep_stages< MessageFilter * >::null_filter NullFilter;
	}

	// run filter
	bool GameApp::RunFilter( HWND hWnd , UINT msg , WPARAM wParam , LPARAM lParam )
	{
		if( m_msgFilterDirt )
		{
			m_msgFilters.clear();
			// filter all
			if( ! m_msgFiltersStage.traversal( _QueueStageFilter< MessageFilter* >( m_msgFilters ) , NullFilter() , NullFilter() ) )
			{
				FATAL_ABORT( _S("Failed to resolve message filter stages.") );
			}
			m_msgFilterDirt = false;
		}

		for( size_t i = 0 , total = m_msgFilters.size() ; i < total ; ++i )
		{
			if( m_msgFilters[i]->Filter( hWnd , msg , wParam , lParam ) )
				return true;
		}
		return false;
	}

	// run ticker
	void GameApp::RunTicker( void )
	{
		COUNTER_GUARD( _S("ticker") );

		if( m_tickerDirt )
		{
			m_tickers.clear();

			// filter all
			if( ! m_tickersStage.traversal( _QueueStageFilter< Ticker* >( m_tickers ) , NullFilter() , NullFilter() ) )
			{
				FATAL_ABORT( _S("Failed to resolve ticker stages.") );
			}

			m_tickerDirt = false;
		}

		for( size_t i = 0 , total = m_tickers.size() ; i < total ; ++i )
		{
			m_tickers[i]->Tick( engine() );
		}
	}

	// win proc for game window
	LRESULT CALLBACK GameApp::MessageProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
	{
		// guarantee to quit program
		if( msg == WM_DESTROY )
		{
			::PostQuitMessage(0);
			return S_OK;
		}

		WNDPROC defProc = DefWindowProc;
		GameApp * gameApp = static_cast<GameApp*>(::GetProp( hWnd , (LPCTSTR)s_procAtom ));

		if( gameApp )
		{
			// for all filters
			if( gameApp->RunFilter( hWnd , msg , wParam , lParam ) )
				return S_OK;

			// get old procedure
			if( gameApp->m_oldProc )
				defProc = gameApp->m_oldProc;
		}
		return defProc( hWnd , msg , wParam , lParam );
	}

	// run game loop
	void GameApp::Run( void * primaryWindow )
	{
		// handle
		HWND hWnd = (HWND)primaryWindow;
		assert( ::IsWindow( hWnd ) == TRUE );

		// redirect window proc
		m_oldProc = (WNDPROC)::GetWindowLongPtr( hWnd , GWLP_WNDPROC );
		::SetProp( hWnd , (LPCTSTR)s_procAtom , this );
		::SetWindowLongPtr( hWnd , GWLP_WNDPROC , (LONG_PTR)MessageProc );

		// start engine
		engine().Initialize();
		{
			// initialize application
			if( !Initialize() )
				return;

			// first frame
			engine().FirstFrame();
			{
				// message loop
				{
					bool bGotMsg;
					MSG  msg;
					msg.message = WM_NULL;
					::PeekMessage( &msg , NULL , 0U , 0U , PM_NOREMOVE );
					
					Counter last = Counter::Now();

					while( WM_QUIT != msg.message )
					{
						bool run = false;
						// Use PeekMessage() so we can use idle time to render the scene. 
						bGotMsg = ( ::PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
						if( bGotMsg )
						{
							// Translate and dispatch the message
							::TranslateMessage( &msg );
							::DispatchMessage( &msg );
						}

						if( ::IsWindow( hWnd ) )
						{ // game loop

							COUNTER_SCOPE( _S("*counter/app") );
							{
								// run ticker before tick
								RunTicker();

 								Counter current = Counter::Now();
 								//if( ( current - last ).ToMilliSecond() > 40 )
								{
									// update frame
									engine().UpdateFrame();
									last = current;
								}
							}
							COUNTER_END();
							// clear states
							CounterObserver::NextFrame();
						}
					}
				}
			}
			// last frame
			engine().LastFrame();

			// finalize application
			Finalize();
		}
		engine().Finalize();

		m_msgFiltersStage.clear();
		m_msgFilters.clear();
		m_tickersStage.clear();
		m_tickers.clear();
	}
} // namespace xeres
